package access

import (
	"encoding/json"
	"time"

	"server/common"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
)

// -------------------------------------------------------------------------------------------------
// 注意：要求客户端发送 Ping 消息，服务端自动响应 Pong
// -------------------------------------------------------------------------------------------------
func (s *AccessService) HandleWebSocket(c *gin.Context) {
	conn, err := s.upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
			Error("failed to upgrade connection to WebSocket")
		return
	}

	common.Logger(common.SERVICE_TYPE_ACCESS).WithField("addr", conn.NetConn().RemoteAddr()).Info(
		"WebSocket connection established")

	connId := uuid.New().String()
	s.connMgr.AddConn(connId, conn) // 将连接添加到连接管理器

	defer func() {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithFields(logrus.Fields{
			"connId": connId,
		}).Info("closing WebSocket connection")
		s.connMgr.RemoveConn(connId)   // 从连接管理器中移除连接
		conn.Close()                   // 关闭连接
		s.sendConnClosedNotify(connId) // 发送连接关闭通知
	}()

	// 建立连接后，如果30秒钟内没有收到消息，则关闭连接
	if err := conn.SetReadDeadline(time.Now().Add(30 * time.Second)); err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
			Error("failed to set read deadline")
		return
	}

	// 处理消息接收和发送
	firstMsgReceived := false
	for {
		messageType, msg, err := conn.ReadMessage()
		if err != nil {
			common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
				Error("failed to read message from WebSocket")
			break
		}

		// 根据消息类型处理
		switch messageType {
		case websocket.TextMessage:
			if err := s.processTextMessage(conn, connId, msg); err != nil {
				common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
					Error("failed to process text message")
				return
			}

			// 收到第一个消息后，清除读取超时设置
			if !firstMsgReceived {
				if err := conn.SetReadDeadline(time.Time{}); err != nil {
					common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
						Error("failed to clear read deadline")
					return
				}
				firstMsgReceived = true // 标记第一次消息已接收
			}

		case websocket.BinaryMessage:
			common.Logger(common.SERVICE_TYPE_ACCESS).Info("received binary message")
			// 处理二进制消息

		case websocket.CloseMessage:
			common.Logger(common.SERVICE_TYPE_ACCESS).Info("received close message")
			return

		case websocket.PingMessage:
			// 自动响应 Pong
			if err := conn.WriteMessage(websocket.PongMessage, nil); err != nil {
				common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).Error("failed to send pong")
				return
			}

		default:
			common.Logger(common.SERVICE_TYPE_ACCESS).
				Warnf("received unknown message type: %d", messageType)
		}
	}
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (s *AccessService) processTextMessage(conn *websocket.Conn, connId string, msg []byte) error {
	var message common.Message
	if err := json.Unmarshal(msg, &message); err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
			Error("failed to unmarshal message")
		return err
	}

	toService, err := getRouteServiceInfo(message)
	if err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
			Error("failed to get route service info")
		return err
	}

	err = s.dispatcher.Dispatch(message, s.serviceInfo, *toService, connId)
	if err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
			Error("failed to dispatch message")
		// 发送错误响应
		res := common.MakeNilResMsg(message.Rid, getResponseCmd(message.Cmd), 1, err.Error())
		handleTextResponse(conn, res)
		return err
	}

	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func handleTextResponse(conn *websocket.Conn, res interface{}) {
	if res == nil {
		return // 如果没有响应，则跳过
	}

	// 将响应转换为 JSON
	data, err := json.Marshal(res)
	if err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).Error("failed to marshal response")
		return
	}

	// 发送响应
	if err := conn.WriteMessage(websocket.TextMessage, data); err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).
			Error("failed to write message to WebSocket")
	}
}

// -------------------------------------------------------------------------------------------------
// 发送连接关闭通知
// -------------------------------------------------------------------------------------------------
func (s *AccessService) sendConnClosedNotify(connId string) {
	notify := common.Message{
		Rid: common.GenerateRandomStr(8),
		Cmd: common.CONNECTION_CLOSED_NOTIFY,
		Data: common.ConnClosedNotifyData{
			ConnId: connId,
		},
	}

	if err := s.dispatcher.Dispatch(
		notify,
		s.serviceInfo,
		common.ServiceInfo{ServiceType: common.SERVICE_TYPE_ONLINE},
		connId,
	); err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).Error(
			"failed to dispatch connection closed notify to online service")
	}

	if err := s.dispatcher.Dispatch(
		notify,
		s.serviceInfo,
		common.ServiceInfo{ServiceType: common.SERVICE_TYPE_ROOM},
		connId,
	); err != nil {
		common.Logger(common.SERVICE_TYPE_ACCESS).WithError(err).Error(
			"failed to dispatch connection closed notify to room service")
	}
}
